package org.hawk.gwt.ppc.reflect.lang;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
 * Root of class loaders hierarchy.
 * @author alex.bereznevatiy@gmail.com
 */
public final class SystemClassLoader extends ClassLoaderDelegate {
	private static final Class<?>[] PRIMITIVE_CLASSES = new Class<?>[]{
			boolean.class,
			byte.class,
			double.class,
			float.class,
			int.class,
			long.class,
			short.class,
			char.class,
			void.class
		};
	
	private static final Class<?>[] PRIMITIVE_ARRAYS = new Class<?>[]{
		boolean[].class,
		byte[].class,
		double[].class,
		float[].class,
		int[].class,
		long[].class,
		short[].class,
		char[].class
	};
	
	private static Map<String,Class<?>> classNames;
	
	public static boolean isPrimitiveClass(String name){
		return getPrimitivesMap().containsKey(name);
	}

	private static Map<String,Class<?>> getPrimitivesMap() {
		if(classNames==null){
			classNames = new HashMap<String, Class<?>>();
			for(Class<?> cls : PRIMITIVE_CLASSES){
				classNames.put(cls.getName(),cls);
			}
			classNames = Collections.unmodifiableMap(classNames);
		}
		return classNames;
	}

	public static Class<?> getPrimitiveClass(String type) {
		return getPrimitivesMap().get(type);
	}
	
	private static Map<String,String> arraysNames;
	
	public static String getArrayTypeName(String name) {
		if (name.indexOf('[') > 0) {
			return "[" + name;
		}
		if(arraysNames==null){
			arraysNames = new HashMap<String, String>();
			for(Class<?> type : PRIMITIVE_ARRAYS){
				arraysNames.put(type.getComponentType().getName(),
						type.getName());
			}
		}
		if(arraysNames.get(name)!=null){
			return arraysNames.get(name);
		}
		return "[L"+name+';';
	}
	
	private static Map<String,Class<?>> arraysTypes;
	
	public static Class<?> getArrayType(String name) {
		if(arraysTypes==null){
			arraysTypes = new HashMap<String, Class<?>>();
			for(Class<?> type : PRIMITIVE_ARRAYS){
				arraysTypes.put(type.getName(),type);
			}
		}
		if(arraysTypes.get(name)!=null){
			return arraysTypes.get(name);
		}
		return null;
	}
	
	private static Map<String,String> arraysComponents;
	
	public static String getArrayComponentName(String name) {
		if(arraysComponents==null){
			arraysComponents = new HashMap<String, String>();
			for(Class<?> type : PRIMITIVE_ARRAYS){
				arraysComponents.put(type.getName(),type.getComponentType().getName());
			}
		}
		if(arraysComponents.get(name)!=null){
			return arraysComponents.get(name);
		}
		if(name.indexOf('[', 1) < 0){
			return name.substring(2,name.length()-1);
		}
		if (name.charAt(0) != '[') {
			throw new IllegalArgumentException("Not a valid array class name: " + name);
		}
		return name.substring(1);
	}
	
	private static SystemClassLoader instance;
	private SystemClassLoader(){
		children = new LinkedList<ClassLoaderDelegate>();
		children.add(new LangClassLoader());
	}
	
	static SystemClassLoader getInstance(){
		if(instance==null){
			instance = new SystemClassLoader();
		}
		return instance;
	}
	
	private List<ClassLoaderDelegate> children;
	
	protected ClassDelegate<?>[] getClasses() {
		return new ClassDelegate[0];
	}

	static void registerClassLoader(ClassLoaderDelegate classLoader) {
		getInstance().children.add(classLoader);
	}

	protected String getPackage() {
		return "";
	}

	public ClassDelegate<?> loadClass(String name)
			throws ClassNotFoundException {
		ClassDelegate<?> cls = findClass(name);
		if(cls==null){
			cls = loadNotReflectableClass(name);
		}
		if(cls==null){
			throw new ClassNotFoundException(name);
		}
		return cls;
	}

	private ClassDelegate<?> findClass(String name) {
		for(ClassLoaderDelegate child : children){
			try {
				return child.loadClass(name);
			}
			catch(ClassNotFoundException ex){}
		}
		return null;
	}

	protected Class<?> getNotReflectableClass(String name) {
		for(ClassLoaderDelegate child : children){
			Class<?> cls = child.getNotReflectableClass(name);
			if(cls!=null)return cls;
		}
		return super.getNotReflectableClass(name);
	}

	protected Class<?>[] getNotReflectableClasses() {
		return PRIMITIVE_CLASSES;
	}

	@Override
	protected Class<?>[] getInterfaces() {
		return new Class[0];
	}
}
